﻿using Drive.LiveExcelHost.Mvc;
using Drive.LiveExcelHost.Services;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Drive.LiveExcelHost
{
    public class HttpService
    {
        public Request Request
        {
            get
            {
                return HttpContext.Current.Request;
            }
        }
        public Response Response
        {
            get
            {
                return HttpContext.Current.Response;
            }
        }

        public void ProcessRequest()
        {
            object output = null;
            var methods = this.GetType().GetMethods();
            MethodInfo method = null;
            foreach (var m in methods)
            {
                var paths = m.GetCustomAttributes<PathAttribute>();
                if (paths.Any())
                {
                    if (paths.Any(x => x.Value == "*"))
                    {
                        method = m;
                    }
                    else if (paths.Any(x => x.Value.Equals(Request.Path)))
                    {
                        method = m;
                    }
                }
            }
            if (method == null)
            {
                Response.StatusCode = 404;
            }
            else
            {
                try
                {
                    output = InvokeMethod(method);
                }
                catch (TargetInvocationException ex)
                {
                    throw ex.InnerException;
                }
                if (output == null)
                {
                    return;
                }
                this.Response.WriteString(JsonConvert.SerializeObject(output, new JsonSerializerSettings
                {
                    ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                }), "application/json");
            }
        }

        private object InvokeMethod(MethodInfo m)
        {
            var parameters = m.GetParameters();
            var json = Request.Method == "POST" ? this.Request.Payload : this.ExpandQuery(Request.PathAndQuery);
            var list = BindParameters(parameters, json);
            if (Request.Method == "POST")
            {
                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i] == null)
                    {
                        list[i] = BindParameters(new ParameterInfo[] { parameters.ElementAt(i) }, this.ExpandQuery(Request.PathAndQuery))[0];
                    }
                }
            }
            return m.Invoke(this, list.ToArray());
        }

        private List<object> BindParameters(ParameterInfo[] parameterInfos, string json)
        {
            if (string.IsNullOrEmpty(json))
            {
                json = "{}";
            }
            List<object> list = new List<object>();
            foreach (var item in parameterInfos)
            {
                Type ts = null;
                if (item.ParameterType.IsGenericType)
                {
                    var tss = item.ParameterType.GetGenericArguments();
                    if (tss.Length > 1)
                    {
                        throw new Exception("err_unsupported_bind");
                    }
                    else
                    {
                        ts = tss[0];
                    }
                }
                else
                {
                    ts = item.ParameterType;
                }
                if (ts.IsPrimitive
                   || ts == typeof(string)
                   || ts == typeof(Guid)
                   || ts == typeof(DateTime)
                   )
                {
                    bool found = false;
                    foreach (var dd in JObject.Parse(json))
                    {
                        if (dd.Key.Equals(item.Name, StringComparison.OrdinalIgnoreCase))
                        {
                            found = true;
                            list.Add(dd.Value.ToObject(ts));
                        }
                    }
                    if (!found)
                    {
                        list.Add(null);
                    }
                }
                else if (item.ParameterType.IsClass)
                {
                    list.Add(JsonConvert.DeserializeObject(json, item.ParameterType));
                    continue;
                }
                else
                {
                    throw new Exception("err_unsupported_bind");
                }
            }
            return list;
        }

        private string ExpandQuery(string pathAndQuery)
        {
            JObject d = d = new JObject();
            var value = pathAndQuery;
            if (value.IndexOf('?') != -1)
                value = value.Split('?')[1];
            if (!string.IsNullOrEmpty(value))
            {
                var kp = value.Split('&');
                foreach (var k in kp)
                {
                    if (!string.IsNullOrEmpty(k))
                    {
                        var kv = k.Split('=');
                        if (kv.Length == 2)
                            d.Add(kv[0], kv[1]);
                    }
                }
            }
            return d.ToString();
        }
    }
}
